﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using Bling.DSL;
using Bling.WPF;
using Bling.WPF.Custom;
using Bling.WPF.Util;
using Bling.Core;
using Bling.Util;
using Bling.Properties;
using Bling.Mixins;
using Bling.Blocks;
using Bling.Shapes;
using Bling.Graphics;
using Bling.Matrices;
using Bling.Angles;
using Bling.Reactive;
using Bling.WPF3D;

namespace Bling.Tutorial {
  public class CubeTest2 : Canvas {
    public class Cube : Canvas { }
    public class CubeBl : CanvasBl<Cube, CubeBl> {
      public readonly FaceBl[] Faces = new FaceBl[6];
      public readonly IntBl CurrentFace;
      public readonly IntBl NextFace;
      public readonly IntBl PreviousFace;
      public readonly DoubleBl AnimateClock;
      public readonly DoubleBl Entrance;
      public CubeBl() : base() { }
      public CubeBl(Expr<Cube> c) : base(c) { }
      public CubeBl(PanelBl Container)
        : base(Container, new Cube()) {
        string ipsum = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ullamcorper bibendum consequat. Vestibulum accumsan convallis nulla, venenatis lobortis neque auctor ut. Quisque dui justo, tempus ut facilisis sit amet, ullamcorper rhoncus lacus. Nunc et elit augue. Integer pretium, enim vitae ultrices sagittis, felis justo congue lacus, non laoreet nulla est sed dui. Nunc facilisis luctus orci, eu commodo nisl condimentum in. Phasellus sollicitudin viverra mauris sit amet elementum. Praesent sit amet nisi vel lectus imperdiet condimentum elementum feugiat justo. Phasellus nec nulla arcu. Aenean aliquet facilisis lorem vitae congue. Morbi vestibulum, nulla eu ultrices molestie, lorem risus convallis orci, vel convallis metus lorem sit amet neque. Ut consectetur luctus sollicitudin. Vestibulum egestas interdum nibh, ut rhoncus augue posuere in. Mauris vitae turpis at augue pulvinar sodales. Aenean enim neque, lobortis sed euismod eleifend, bibendum et ligula. Suspendisse volutpat, neque at sodales eleifend, felis dui pulvinar mi, sed congue metus metus at elit. Sed massa nibh, tincidunt dapibus vulputate in, ullamcorper quis urna. Quisque varius dignissim erat eget cursus.
Nulla facilisi. Nulla tempus fermentum leo. Phasellus elit nunc, venenatis a porttitor eget, dapibus quis ligula. Vestibulum dignissim commodo ipsum sit amet venenatis. Proin posuere massa a odio laoreet vitae dignissim nunc aliquet. Nam aliquet euismod semper. Nulla tincidunt, lectus id fringilla tincidunt, purus est adipiscing ante, aliquam placerat augue enim sed nibh. Cras sit amet enim ligula, eu commodo dui. Ut pellentesque, massa ut porttitor ultricies, ligula lacus scelerisque dolor, id ornare tortor arcu eu urna. Proin id elit arcu. Sed vel nulla vel magna aliquet scelerisque at sit amet massa. Donec nec rutrum mauris. Donec fringilla, lacus eu fringilla vulputate, leo justo mattis est, nec feugiat sem mi ultrices purus. Vivamus egestas odio ac ligula rhoncus eu interdum nunc dapibus. Donec lectus purus, cursus mollis mollis eu, dignissim ut mauris. Aliquam vitae elit turpis, et dignissim ante. Duis id justo ipsum, at pellentesque turpis. In a nulla lacus. Vivamus nec scelerisque justo.
Duis et lobortis magna. In quis tempus sapien. Aenean quam ligula, mollis venenatis bibendum mollis, interdum nec magna. Aliquam placerat diam velit, at pharetra enim. Aliquam erat volutpat. Aenean imperdiet aliquet quam, ut suscipit tellus luctus id. Donec bibendum, enim sed elementum pharetra, quam nisi iaculis nibh, quis feugiat ipsum leo sit amet tellus. Morbi faucibus metus id lectus rhoncus sit amet vehicula magna pulvinar. Ut ullamcorper, urna et accumsan gravida, lectus dolor blandit tellus, vitae lacinia sem mauris eget ligula. Donec sit amet dictum mauris. Praesent consectetur convallis leo quis aliquet. Mauris a fermentum erat. Curabitur ut commodo turpis. Curabitur dignissim lacinia mi, at imperdiet lacus venenatis nec. Aliquam id placerat eros. Aliquam bibendum nibh id nisi tempor laoreet. Maecenas convallis pulvinar erat, nec feugiat lectus ultricies in. Nunc rhoncus venenatis libero quis bibendum.
Suspendisse eleifend dignissim nibh, id vehicula ante feugiat at. Aliquam suscipit venenatis nibh, in mattis nisi pellentesque ac. Morbi venenatis lacus eu arcu posuere in lacinia enim iaculis. Aliquam vel erat vitae nisl congue convallis sit amet ac augue. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque justo arcu, facilisis vitae ullamcorper non, egestas in mi. In hac habitasse platea dictumst. Aliquam pharetra consectetur dui, non convallis enim fermentum id. Integer et velit urna. Sed vitae lorem purus. Sed cursus leo sit amet enim cursus fringilla. Aenean vulputate consectetur tristique. Praesent vulputate leo quis velit laoreet tincidunt. Phasellus ut lectus at diam consequat viverra. Pellentesque sollicitudin, ante iaculis ornare venenatis, massa mi suscipit arcu, sit amet fringilla sapien nibh id ipsum. Phasellus euismod nunc ut justo fermentum ultricies. Maecenas vitae nisl ut eros convallis vestibulum quis quis lorem. Quisque leo felis, porttitor lobortis vestibulum sed, sollicitudin sed massa.
Nam volutpat enim nibh. Nunc sit amet ipsum aliquam ante suscipit vehicula in sed mi. Pellentesque vulputate elit id ipsum dictum hendrerit. Etiam dapibus eros eu orci semper in luctus sapien lobortis. Etiam auctor consequat elit, id viverra turpis bibendum in. Maecenas hendrerit dignissim quam gravida cursus. Nullam dictum purus ut neque interdum dapibus aliquet risus commodo. Pellentesque quis tortor a arcu convallis condimentum. Mauris hendrerit dictum libero vel semper. Morbi malesuada aliquet augue, eu semper eros fringilla ac. Phasellus vitae mi nibh. Nulla dignissim leo eu arcu lacinia et feugiat est tempus. Nam quis arcu tincidunt risus suscipit rhoncus quis id orci. Vestibulum laoreet ornare nisl, ac facilisis felis mollis congue. Ut varius fringilla tincidunt. Phasellus nec tellus vitae nibh viverra imperdiet. Nunc eu turpis neque. Maecenas quis massa id purus malesuada lacinia sit amet eu justo.";

        new CanvasBl(this) {
          Size = this.Size,
          LeftTop = 0,
          ZIndex = 1,
          Background = Brushes.White,
        };
        var UseSize = new Size(500, 500);

        Entrance = this.CurrentValue.Property(0d.Bl());
        CurrentFace = this.CurrentValue.Property(0.Bl());
        PreviousFace = this.CurrentValue.Property(0.Bl());
        NextFace = this.CurrentValue.Property(0.Bl());
        AnimateClock = this.CurrentValue.Property(0d.Bl());

        var AnimateAxis = this.CurrentValue.Property(new Double3Bl(0, 1, 0));
        var AnimateAngle = this.CurrentValue.Property(0.PI());
        var EnabledFace = this.CurrentValue.Property(0.Bl());

        // ZIndex = 0, not shown
        for (int i = 0; i < 6; i++) {
          var face = new FaceBl(i, this);
          Faces[i] = face; //.Add(face);
          var label = new TextBoxBl(face) {
            CenterPosition = face.CenterSize,
            Font = { Weight = FontWeights.SemiBold, Size = 40 },
            Text = 
            "Face : " + i + " Face : " + i +
            "\n" + ipsum,
            Width = .9 * face.Width,
          };
          face.Background = i.SelectColor(); // new ImageBrushBl() { ImageSource = Backgrounds.Table(i % Backgrounds.Length) };
        }

        {
          //var Rotate = this.CurrentValue.Property(QuaternionBl.Identity);
          var Rotate = new QuaternionBl(AnimateAxis, AnimateAngle * AnimateClock);
          Double3Bl CameraPosition = new Double3Bl(0, 0, -2 /** (1d / Zoom)*/);
          var Viewport = new Viewport3DBl(this) {
            Size = 100,
            ZIndex = (AnimateClock > 0).Condition(2, 0.Bl()),
            CenterPosition = this.CenterSize,
            Camera = new PerspectiveCameraBl() {
              Position = CameraPosition,
              Direction = { Look = new Double3Bl(0, 0, +1), Up = new Double3Bl(0, 1, 0) },
            },
            IsHitTestVisible = false,
          };
          Viewport.RenderTransform.Scale = this.Size / 100;
          new ModelVisual3DBl(Viewport) {
            Content = new DirectionalLightBl() {
              Direction = new Double3Bl(0, 0, +1),
              Color = Colors.White,
            },
          }; // let there be light.

          var cube = new CubeMesh();
          //RenderTargetBitmap bitmap0 = new RenderTargetBitmap(

          ImageBrushBl PrevBrush = new ImageBrushBl(), NextBrush = new ImageBrushBl();
          Action UpdateBrush;
          {
            Func<VisualBrushBl, ImageBrushBl, Action> F = (OldBrush, NewBrush) => {
              // render trget bitmap
              RenderTargetBitmap Rt = new RenderTargetBitmap((int)UseSize.Width, (int)UseSize.Height, 96, 96, PixelFormats.Default);
              var vF = OldBrush.AsFunc;

              NewBrush.ImageSource = Rt;
              return () => {
                DrawingVisual dv = new DrawingVisual();
                using (DrawingContext ctx = dv.RenderOpen()) {
                  ctx.DrawRectangle(vF(), null, new Rect(new Point(), UseSize));
                }
                Rt.Render(dv);
              };
            };
            var PrevBrush0 = new VisualBrushBl() { Visual = Faces.Table(PreviousFace) };
            var NextBrush0 = new VisualBrushBl() { Visual = Faces.Table(NextFace) };
            var a0 = F(PrevBrush0, PrevBrush);
            var a1 = F(NextBrush0, NextBrush);
            UpdateBrush = () => { a0(); a1(); };
          }

          for (int i = 0; i < 6; i++) {
            // Define a directional light in the same direction of the face. 
            {
              BrushBl Brush;
              if (i == 0) Brush = PrevBrush;
              else if (i == 3) Brush = Brushes.White;
              else Brush = NextBrush; // (EnabledFace == i).Condition((BrushBl)NextBrush, Brushes.White.Bl());



              new ModelVisual3DBl(Viewport) {
                Transform = new MatrixTransform3DBl() {
                  Matrix = Matrix4Bl.MakeScale(.5858) * Rotate,
                },
                Content = new GeometryModel3DBl() {
                  Geometry = new MeshGeometry3DBl(cube.GetFace(i)),
                  Material = new DiffuseMaterialBl() {
                    Brush = Brush,
                    Knob = 1d,
                  },
                },
              };
            }
          }
          var Complete = IntBl.Assign(CurrentFace, NextFace);
          Action<ButtonBl, IntBl[], DoubleBl, bool> DoAnimate = (doRotate, Table, InPi, IsX) => {
            doRotate.Content = "Rotate";
            doRotate.ZIndex = 100;
            AnimateAngle[doRotate.Click1] = InPi.PI();
            AnimateAxis[doRotate.Click1] = new Double3Bl(IsX ? 1 : 0, IsX ? 0 : 1, 0);


            var Dest = Table.Table(CurrentFace);
            EnabledFace[doRotate.Click1] = Table[0];
            PreviousFace[doRotate.Click1] = CurrentFace;
            NextFace[doRotate.Click1] = Dest;
            doRotate.Click1.SubscribeForever(UpdateBrush);

            AnimateClock[doRotate.Click1].Animate().Duration(500).Tween(clock => clock.Ease.Quad.InOut).StopOnCompleted(true).Completed((x) => {
              Complete();
            }).To = 1d;
          };
          for (int i = 0; i < 6; i++) {
            CanvasBl Canvas = Faces[i];
            DoAnimate(new ButtonBl(Canvas) {
              CenterTop = new Double2Bl(Canvas.Width / 2d, 0),
            }, new IntBl[] {
            /* 0 */ 5,
            /* 1 */ 0,
            /* 2 */ 5,
            /* 3 */ 1,
            /* 4 */ 5,
            /* 5 */ 3,
          }, .5, true);
            DoAnimate(new ButtonBl(Canvas) {
              CenterBottom = new Double2Bl(Canvas.Width / 2d, Canvas.Height),
            }, new IntBl[] {
            /* 0 */ 1,
            /* 1 */ 3,
            /* 2 */ 1,
            /* 3 */ 5,
            /* 4 */ 1,
            /* 5 */ 0,
          }, -.5, true);
            DoAnimate(new ButtonBl(Canvas) {
              LeftCenter = new Double2Bl(0d, Canvas.Height / 2),
            }, new IntBl[] {
            /* 0 */ 2,
            /* 1 */ 2,
            /* 2 */ 3,
            /* 3 */ 4,
            /* 4 */ 0,
            /* 5 */ 2,
          }, -.5, false);
            DoAnimate(new ButtonBl(Canvas) {
              RightCenter = new Double2Bl(Canvas.Width, Canvas.Height / 2),
            }, new IntBl[] {
            /* 0 */ 4,
            /* 1 */ 4,
            /* 2 */ 0,
            /* 3 */ 2,
            /* 4 */ 3,
            /* 5 */ 4,
          }, +.5, false);
          }
        }
        { 
          var Rotate = this.CurrentValue.Property(QuaternionBl.Identity);
          var Translate = this.CurrentValue.Property(Double3Bl.Zero);
          var StopSpin = .8d.Bl();

          Double3Bl CameraPosition = new Double3Bl(0, 0, -2 /** (1d / Zoom)*/);
          var Viewport = new Viewport3DBl(this) {
            Size = 100,
            ZIndex = (Entrance > 0 & Entrance < 1d).Condition(2, 0.Bl()),
            CenterPosition = this.CenterSize,
            Camera = new PerspectiveCameraBl() {
              Position = CameraPosition,
              Direction = { Look = new Double3Bl(0, 0, +1), Up = new Double3Bl(0, 1, 0) },
            },
            IsHitTestVisible = false,
          };
          Viewport.RenderTransform.Scale = this.Size / 100;
          new ModelVisual3DBl(Viewport) {
            Content = new DirectionalLightBl() {
              Direction = new Double3Bl(0, 0, +1),
              Color = Colors.White,
            },
          }; // let there be light.
          //Viewport.Effect = new DropShadowEffectBl();

          Action UpdateBrushes = () => { };
          Func<FaceBl, ImageBrushBl> F = (Face) => {
            // render trget bitmap
            var OldBrush = new VisualBrushBl() { Visual = Face };
            RenderTargetBitmap Rt = new RenderTargetBitmap((int)UseSize.Width, (int)UseSize.Height, 96, 96, PixelFormats.Default);
            var vF = OldBrush.AsFunc;
            var OldUpdate = UpdateBrushes;
            UpdateBrushes = () => {
              DrawingVisual dv = new DrawingVisual();
              using (DrawingContext ctx = dv.RenderOpen()) {
                ctx.DrawRectangle(vF(), null, new Rect(new Point(), UseSize));
              }
              Rt.Render(dv);
              OldUpdate();
            };
            return new ImageBrushBl() { ImageSource = Rt };
          };
          var cube = new CubeMesh();

          DoubleBl DoScale = Entrance.Square;
          DoScale = (Entrance > StopSpin).Condition(
            Entrance.Pow(2d + 5d * (Entrance - StopSpin) / (1d - StopSpin))
            //(0d + StopSpin.Square) + 
            //(1d - StopSpin.Square) * ((Entrance - StopSpin) / (1d - StopSpin)).Pow(4) +
            //0
            , DoScale);

          for (int i = 0; i < 6; i++) {
            new ModelVisual3DBl(Viewport) {
              Transform = new MatrixTransform3DBl() {
                Matrix = Matrix4Bl.MakeTranslate(Translate) * 
                Matrix4Bl.MakeScale(.5858 * DoScale) * Rotate,
              },
              Content = new GeometryModel3DBl() {
                Geometry = new MeshGeometry3DBl(cube.GetFace(i)),
                Material = new DiffuseMaterialBl() {
                  Brush = F(Faces.Table((CurrentFace + i) % 6)),
                  Knob = 1d,
                },
              },
            };
          }
          // work on in/out transition
          var enter = new ButtonBl(this) {
            Content = "Enter",
            RightBottom = this.Size, ZIndex = 100,
          };
          // update brushes when we click. 
          Entrance[enter.Click1].Animate().Duration(1000).Delay(200).StopOnCompleted(false).Tween(d => d).To = 
            (Entrance[enter.Click1] > 0d).Condition(0d, 1d); // use scale as our clock. 
          enter.Click1.SubscribeForever(UpdateBrushes);
          var DoRotate = (Entrance / StopSpin).Min(1d);

          Rotate.Bind =
            new QuaternionBl(new Double3Bl(1, 1, 1), +1d * (DoRotate * 2d).PI()) *
            //new QuaternionBl(new Double3Bl(1, 0, 0), -1d * ((StopSpin * Entrance).Min(2d)).PI()) *
            //new QuaternionBl(new Double3Bl(0, 0, 1), (+2d * Scale).PI()) *
            QuaternionBl.Identity;

          Translate.Bind = DoRotate.Bezier(
            new Double3Bl(.9, 0, 0),
            new Double3Bl(.9 * 1d / 2d, +1.0, 0),
            //new Double3Bl(.9 * 1d / 3d, -1.0, 0),
            new Double3Bl(0, 0, 0));


          //Translate.Bind = ((StopSpin * Entrance).Min(2d) / 2d).Lerp(new Double3Bl(.9, 0, 0), Double3Bl.Zero);
        }
      }
    }
    public class Face : Canvas { }
    public class FaceBl : CanvasBl<Face, FaceBl> {
      public FaceBl() : base() { }
      public FaceBl(Expr<Face> c) : base(c) { }
      public FaceBl(int Idx, CubeBl Container) : base(Container, new Face()) {
        // the page is...
        Size = Container.Size;
        ZIndex = ((Idx == Container.CurrentFace) & (Container.AnimateClock == 0) & (Container.Entrance == 1d)).Condition(2.Bl(), 0.Bl());
        ClipToBounds = true;
      }
    };


    public CubeTest2() {
      if (true) {
        var cube = new CubeBl(this.Bl());
        cube.LeftTop = 0; cube.Size = this.Bl().Size;
        return;
      }
    }
  }
}